package admin

import (
	"errors"
	"exercise/application/yiran/app/libs"
	"exercise/application/yiran/app/model"
	"exercise/application/yiran/app/request/adminRequest"
	"fmt"
	"github.com/gin-gonic/gin"
)

type enrollStudentService struct {
	req      *gin.Context
	schoolId any
}

// InitStudent  初始化学生
func (rcv *enrollStudentService) InitStudent(request *adminRequest.EnrollStudentInitRequest) (total int, err error) {
	rule := new(model.EnrollRule)
	detail := make(map[string]interface{}, 0)
	err = model.NewDb("").FindOneMap(rule, "id = ?", []interface{}{request.RuleId}, "id,year,lq_name,lq_range,lq_field,lq_type,pid,tj_type", &detail)

	if err != nil {
		return 0, err
	}

	sql, where := NewEnrollRuleService(rcv.req).GetLqRangeStudent(detail["lq_range"].(string))
	sql += " AND year = ? AND school_id = ?"
	where = append(where, detail["year"], rcv.schoolId)

	//查询学生
	list := make([]model.ExamStudent18817, 0)

	query := model.NewDb("").TXQuery()
	err = query.Where(sql, where...).Order("id DESC").Find(&list).Error
	if err != nil {
		return 0, err
	}

	allData := make([]map[string]interface{}, 0)
	for _, val := range list {
		insert := make(map[string]interface{}, 0)
		insert["school_id"] = rcv.schoolId
		insert["year"] = request.Year
		insert["sfzh"] = val.Sfzh
		insert["ksh"] = val.Ksh
		insert["stu_name"] = val.Xm
		insert["lqzy"] = ""
		insert["lq_score"] = 0
		insert["score"] = 0
		insert["create_time"] = libs.CurrentTime()
		insert["lq_status"] = 1
		insert["stu_id"] = val.ID
		insert["rule_id"] = request.RuleId
		insert["lq_type"] = detail["lq_name"]
		insert["rule_pid"] = detail["pid"]
		insert["type_id"] = request.TypeId
		allData = append(allData, insert)
		if len(allData) == 200 {
			err = query.Model(model.EnrollStudentModel).Create(allData).Error
			if err != nil {
				query.Rollback()
				return 0, err
			}
			allData = nil
		}
	}

	if len(allData) > 0 {
		err = query.Model(model.EnrollStudentModel).Create(allData).Error
		if err != nil {
			query.Rollback()
			return 0, err
		}
	}
	query.Commit()

	return len(list), err
}

// UpdateScoreRanking 更新排名(更新成学生成绩表里的)
func (rcv *enrollStudentService) UpdateScoreRanking(request *adminRequest.UpdateScoreRankingRequest) (err error) {

	//将分数表里的排名更新到录取预录取表中
	sql := "UPDATE " + model.StudentScoreTable + " AS s "
	sql += " INNER JOIN " + model.EnrollStudentTable + " AS e ON e.stu_id = s.stu_id AND e.year = s.year AND e.type_id = s.type_id"
	sql += " SET e.score_rank = s.ranking,e.lq_score = s.score_total,e.score = s.score_zycs"
	sql += " WHERE e.type_id = ? AND e.year = ? AND e.rule_id = ?"

	where := make([]interface{}, 0)
	where = append(where, request.TypeId, request.Year, request.RuleId)
	err = model.NewDb("").Query().Exec(sql, where...).Error
	if err != nil {
		return err
	}

	//更新免考人员名次

	return err
}

// EarlyDecision 预录取
func (rcv *enrollStudentService) EarlyDecision(request *adminRequest.EarlyDecisionRequest) (res interface{}, err error) {

	year := request.Year
	ruleId := request.RuleId
	typeId := request.TypeId

	println(year)
	println(typeId)

	//获取录取规则
	enrollRule := new(model.EnrollRule)
	err = model.NewDb("").FindOne(enrollRule, "id = ?", []interface{}{ruleId}, "*", []string{})

	if err != nil {
		return nil, err
	}

	if enrollRule.LqType == "" {
		return nil, errors.New("请先配置规则")
	}

	lqType := make(map[string]interface{}, 0)
	err = libs.JsonDecode([]byte(enrollRule.LqType), &lqType)
	/**

	按志愿录取
		1.循环招生计划表
		2.查询学生的第几志愿符合招生计划,只要有一个符合的就录取
	*/

	if lqType["type"] == "score_first" {
		res, err = rcv.scoreFirst(request, lqType, enrollRule)
	} else if lqType["type"] == "zhiy_first" {
		res, err = rcv.volunteerFirst(request, lqType, enrollRule)
	}

	return
}

// scoreFirst 按分数录取
func (rcv *enrollStudentService) scoreFirst(request *adminRequest.EarlyDecisionRequest, lqType map[string]interface{}, enrollRule *model.EnrollRule) (res interface{}, err error) {
	query := model.NewDb("").Query()
	//1.录取一般按照成绩优先录取,成绩排序字段排序,循环学生
	studentList := make([]model.EnrollToStudent, 0)
	sql := "year = ? AND rule_id = ? AND type_id = ? AND lq_status = ? AND is_affirm != ? "
	where := make([]interface{}, 0)
	where = append(where, request.Year, request.RuleId, request.TypeId, 1, 3)
	err = query.Where(sql, where...).Preload("Student").Order("id DESC").Find(&studentList).Error

	//2.检测学校的招生计划列表：包括专业名称  计划招生数
	majorPlanList := make([]map[string]interface{}, 0)
	err = query.Model(model.MajorPlanModel).Where("year = ?  ", request.Year).Select("major_name,lq_num").Find(&majorPlanList).Error
	majorPlanListTmp := libs.ArrayColumn(majorPlanList, "major_name")

	//3.专业被录取的人数
	lqzyList := make([]map[string]interface{}, 0)

	sql = "year = ? AND rule_id = ? AND type_id = ? AND lq_status = ? AND is_affirm != ? "
	err = query.Model(model.EnrollStudentModel).Select("lqzy,COUNT(id) as lq_total").Where(sql, request.Year, request.RuleId, request.TypeId, 2, 3).Group("lqzy").Find(&lqzyList).Error
	lqzyListTmp := libs.ArrayColumn(lqzyList, "lqzy")

	//未考试学生
	unKsStudent := make(chan int64, 0)
	go rcv.UpdateStudentStatus(unKsStudent, "未参加校考，不予录取")
	//不服从调剂学生
	noTjStudent := make(chan int64, 0)
	go rcv.UpdateStudentStatus(noTjStudent, "不服从调剂,退档")
	//无调剂专业的学生
	noZyStudent := make(chan int64, 0)
	go rcv.UpdateStudentStatus(noTjStudent, "无调剂专业,退档")

	//4.查询学生的第几志愿与计划是相同的，该学生就被第几志愿录取
	for _, stu := range studentList {
		if stu.Student.ID == 0 { //学生不存在
			unKsStudent <- stu.ID
			continue
		}

		if stu.Score == 0 && stu.ScoreRank == 0 { //免考学生,分数=0,但是存在名次且排名改成1
			unKsStudent <- stu.ID
			continue
		}

		//录取专业匹配
		var lqzyName string
		var zySort int

		for i := 1; i <= 6; i++ {
			bkzyKey := fmt.Sprintf("bkzy%d_zh", i)
			bkzy := stu.Student.GetBkzyFields()
			majorName, _ := bkzy[bkzyKey]
			if majorName == "" { //学生为填报志愿
				continue
			}

			if majorPlanListTmp[majorName] == nil {
				return nil, errors.New(majorName + ",专业计划未设置,请核实")
			}

			var lqTotal int64
			//该专业的已录取数
			if lqzyListTmp[majorName] != nil {
				lqTotal = lqzyListTmp[majorName]["lq_total"].(int64)
			}

			//已录取数大于专业计划数,表示计划已满
			if lqTotal >= int64(majorPlanListTmp[majorName]["lq_num"].(int)) {
				continue
			}

			lqzyName = majorName
			zySort = i
			break
		}

		println(456)
		//修改改考生的录取状态
		if lqzyName != "" && zySort != 0 { //填报志愿匹配计划,则学生就被第几志愿录取
			query.Model(model.EnrollStudentModel).Where("id = ?", stu.ID).Updates(map[string]interface{}{
				"lq_status": 2,
				"lq_type":   fmt.Sprintf("第%d志愿录取", zySort),
				"lqzy":      lqzyName,
			})
			//修改已录取数
			rcv.Lqzyed(lqzyListTmp, lqzyName)
			continue
		}

		println(2)

		//学生志愿未匹配到,检测是否支持调剂
		if stu.Student.IsTj == 0 { //不支持调剂
			/*query.Model(model.EnrollStudentModel).Where("id = ?", stu.ID).Updates(map[string]interface{}{"lq_status": 3, "lq_type": "不服从调剂,退档"})*/
			noTjStudent <- stu.ID
			continue
		}
		println(3)
		if enrollRule.TjType == 3 { //退档
			/*query.Model(model.EnrollStudentModel).Where("id = ?", stu.ID).Updates(map[string]interface{}{"lq_status": 3, "lq_type": "不服从调剂,退档"})*/
			noTjStudent <- stu.ID
			continue
		}
		println(4)
		tjzyList := make([]map[string]interface{}, 0)
		//检测是否存在未录取满员的专业
		for _, plan := range majorPlanListTmp {
			//该专业的已录取总数
			var lqTotal int64
			if lqzyListTmp[plan["major_name"].(string)] != nil {
				lqTotal = lqzyListTmp[plan["major_name"].(string)]["lq_total"].(int64)
			}

			//该专业的录取计划数
			lqNum := plan["lq_num"].(int)
			if lqTotal > int64(lqNum) {
				continue
			}
			tjItem := map[string]interface{}{
				"major_name": plan["major_name"],
				"now_num":    lqNum,
				"lq_num":     lqNum,
			}
			tjzyList = append(tjzyList, tjItem)
		}

		if tjzyList == nil {
			/*query.Model(model.EnrollStudentModel).Where("id = ?", stu.ID).Updates(map[string]interface{}{"lq_status": 3, "lq_type": "无调剂专业,退档"})*/
			noZyStudent <- stu.ID
			continue
		}
		println(5)
		if enrollRule.TjType == 1 { //手动调整
			return map[string]interface{}{
				"tjzyList": tjzyList,
				"is_tj":    1,
				"stu":      stu.Student,
			}, nil
		}

		//自动分配的是招生计划中为招满的专业
		if enrollRule.TjType == 2 { //自动分配
			//自动分配的专业
			lqzy := rcv.RandomZy(lqzyList)
			query.Model(model.EnrollStudentModel).Where("id = ?", stu.ID).Updates(map[string]interface{}{
				"lq_status": 2,
				"lq_type":   fmt.Sprintf("分数优先,随机补录", zySort),
				"lqzy":      lqzy,
			})
			//计算数量
			rcv.Lqzyed(lqzyListTmp, lqzy)
			continue
		}
	}

	go func(unKsStudent, noTjStudent, noZyStudent chan int64) {
		close(unKsStudent)
		close(noTjStudent)
		close(noZyStudent)
	}(unKsStudent, noTjStudent, noZyStudent)

	//全部人员统计
	allTotal := len(studentList)
	//已统计人员统计
	var total int64
	sql = "year = ? AND rule_id = ? AND type_id = ? AND lq_status > ? AND is_affirm != ? "
	w := make([]interface{}, 0)
	w = append(w, request.Year, request.RuleId, request.TypeId, 1, 3)
	err = query.Model(studentList).Where(sql, w...).Count(&total).Error

	return map[string]interface{}{
		"is_tj":     0,
		"tjzyList":  "",
		"stu":       "",
		"all_total": allTotal,
		"pro_total": total,
	}, err
}

// volunteerFirst 按志愿录取
func (rcv *enrollStudentService) volunteerFirst(request *adminRequest.EarlyDecisionRequest, lqType map[string]interface{}, enrollRule *model.EnrollRule) (res interface{}, err error) {

	//1.获取招生计划中的专业
	//2.循环专业列表：学生的第几志愿与招生计划的专业相等,则该考生就是被第几志愿录取

	return nil, err
}

// UpdateStudentStatus 修改学生状态
func (rcv *enrollStudentService) UpdateStudentStatus(studentList chan int64, lqType string) bool {
	//lq_type:不服从调剂,退档
	query := model.NewDb("").Query()
	stu := make([]int64, 0)
	for id := range studentList {
		stu = append(stu, id)
	}
	if len(stu) > 0 {
		query.Model(model.EnrollStudentModel).Where("id IN ?", stu).Updates(map[string]interface{}{"lq_status": 3, "lq_type": "不服从调剂,退档"})
	}
	return true
}

// Lqzyed 修改已录取数
func (rcv enrollStudentService) Lqzyed(lqzyListTmp map[string]map[string]interface{}, lqzyName string) {

	lqTmp, ok := lqzyListTmp[lqzyName]
	if ok {
		lqTmp["lq_total"] = lqTmp["lq_total"].(int64) + 1
	} else {
		tmp := make(map[string]interface{}, 2)
		tmp["major_name"] = lqzyName
		tmp["lq_total"] = 1
		lqzyListTmp[lqzyName] = tmp
	}
}

// RandomZy 随机分配
func (rcv *enrollStudentService) RandomZy(list []map[string]interface{}) string {
	index := libs.RandomInt(len(list))
	return list[index-1]["major_name"].(string)
}

func NewEnrollStudentService(c *gin.Context) *enrollStudentService {
	schoolId, _ := c.Get("schoolId")
	return &enrollStudentService{
		req:      c,
		schoolId: schoolId,
	}
}
